package framework
{
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	import framework.ctx.BeanDefinition;
	import framework.ctx.CtxConfiguration;
	import framework.ctx.CtxCore;
	import framework.ctx.IConfigurableCtx;
	import framework.ctx.ICtx;
	import framework.ctx.ICtxLocator;
	import framework.ctx.IHierarchicalCtx;
	import framework.ctx.IUIHook;

	import mx.events.FlexEvent;

	use namespace flash_proxy;

	[Event(name="propertyChange", type="mx.events.PropertyChangeEvent")]
	[Bindable(event="propertyChange")]
	public dynamic class Ctx extends Proxy implements IHierarchicalCtx, IConfigurableCtx,
		IEventDispatcher, ICtxLocator, IUIHook
	{
		public static var contexts:Object = {};

		public static var contextInitialized:Object = {};

		public function Ctx(contextName:String = "default")
		{
			_ctxName = contextName;
			if (!contextInitialized[contextName])
			{
				configureCtx();
				var app:Object = Utils.getApplication();
				if (app.initialized)
				{
					initCtx();
				}
				else
				{
					app.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
				}
				contextInitialized[contextName] = true;
			}
		}

		virtual public function preInitCtx():void
		{
		}

		private function creationCompleteHandler(e:Event):void
		{
			initCtx();
		}

		private var _ctxName:String;

		private var nextNameArray:Array;

		virtual public function configureCtx():void
		{
		}

		virtual public function initCtx():void
		{
		}

		public function addEventListener(type:String, listener:Function, useCapture:Boolean =
										 false, priority:int = 0, useWeakReference:Boolean =
										 false):void
		{
			ctx.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}

		public function containsBean(clazzOrId:Object):Boolean
		{
			return ctx.containsBean(clazzOrId);
		}

		public function get ctx():ICtx
		{
			var ctx:CtxCore = contexts[_ctxName];
			if (!ctx)
			{
				ctx = new CtxCore;
				contexts[_ctxName] = ctx;
			}
			return ctx;
		}

		public function get ctxConfiguration():CtxConfiguration
		{
			return IConfigurableCtx(ctx).ctxConfiguration;
		}

		public function set ctxConfiguration(v:CtxConfiguration):void
		{
			IConfigurableCtx(ctx).ctxConfiguration = v;
		}

		public function get ctxName():String
		{
			return _ctxName;
		}

		public function set ctxName(v:String):void
		{
			_ctxName = v;
		}

		public function dispatchEvent(event:Event):Boolean
		{
			return ctx.dispatchEvent(event);
		}

		public function fetchBean(clazz:Class, idToCreate:String = null):*
		{
			return ctx.fetchBean(clazz, idToCreate);
		}

		public function getBean(clazzOrId:Object):*
		{
			return ctx.getBean(clazzOrId);
		}

		public function getBeanDefinitions(clazzOrId:Object):Array
		{
			return ctx.getBeanDefinitions(clazzOrId);
		}

		public function getBeans():Object
		{
			return ctx.getBeans();
		}

		public function getIdsOfType(clazz:Class):Array
		{
			return ctx.getIdsOfType(clazz);
		}

		public function hasEventListener(type:String):Boolean
		{
			return ctx.hasEventListener(type);
		}

		public function get parentCtx():ICtx
		{
			return IHierarchicalCtx(ctx).parentCtx;
		}

		public function set parentCtx(v:ICtx):void
		{
			IHierarchicalCtx(ctx).parentCtx = v;
		}

		public function register(instanceOrClass:Object, id:String = null, resource:Boolean =
								 false):BeanDefinition
		{
			return IConfigurableCtx(ctx).register(instanceOrClass, id, resource);
		}

		public function registerBeanDefinition(beanDefinition:BeanDefinition):BeanDefinition
		{
			return IConfigurableCtx(ctx).registerBeanDefinition(beanDefinition);
		}

		public function removeEventListener(type:String, listener:Function, useCapture:Boolean =
											false):void
		{
			ctx.removeEventListener(type, listener, useCapture);
		}

		public function unregister(id:String):void
		{
			return IConfigurableCtx(ctx).unregister(id);
		}

		public function willTrigger(type:String):Boolean
		{
			return ctx.willTrigger(type);
		}

		flash_proxy override function callProperty(name:*, ... rest):*
		{
			return getBean(name).apply(this, rest)
		}

		flash_proxy override function deleteProperty(name:*):Boolean
		{
			var r:Boolean = containsBean(name);
			if (r)
				unregister(name);
			return r;
		}

		flash_proxy override function getProperty(name:*):*
		{
			return getBean(name);
		}

		flash_proxy override function hasProperty(name:*):Boolean
		{
			return containsBean(name);
		}

		/**
		 *  @private
		 */
		flash_proxy override function nextName(index:int):String
		{
			return nextNameArray[index - 1];
		}

		/**
		 *  @private
		 */
		flash_proxy override function nextNameIndex(index:int):int
		{
			if (index == 0)
			{
				nextNameArray = [];
				for (var op:String in ctx.getBeans())
				{
					nextNameArray.push(op);
				}
			}
			return index < nextNameArray.length ? index + 1 : 0;
		}

		flash_proxy override function nextValue(index:int):*
		{
			return ctx.getBeans()[nextNameArray[index - 1]];
		}

		flash_proxy override function setProperty(name:*, value:*):void
		{
			register(value, name);
		}

		public function enableUIHook():void
		{
			IUIHook(ctx).enableUIHook();
		}

		public function disableUIHook():void
		{
			IUIHook(ctx).disableUIHook();
		}
	}
}